package functionroots.methods;

import functionroots.primitives.RealFunction;
import functionroots.primitives.RootFinder;

/**
 * Implementa o método da posição falsa para a obtenção de zeros de funções
 * de uma variável real.
 * Implements the false position numerical method for finding roots of real,
 * single-variable functions.
 * @author orion
 */
public class FalsePosition extends RootFinder<IntervalData> {

    /**
     * Instancia o método da posição falsa com uma função, precisão e número
     * máximo de iterações pré-definidos.
     * @param function a função alvo.
     * @param precision a precisão pretendida.
     * @param maxIterations o número máximo de iterações.
     */
    public FalsePosition(RealFunction function, long maxIterations, double precision) {
        super(function, precision, maxIterations);
    }

    /**
     * Instancia o método da posição falsa com uma função e precisão
     * pré-definidas.
     * @param function a função alvo.
     * @param precision a precisão pretendida.
     */
    public FalsePosition(RealFunction function, double precision) {
        super(function, precision, 0);
    }

    @Override
    public double scan(IntervalData data) throws IllegalArgumentException {
        double a = data.a, b = data.b;
        double fa = getFunction().value(a);
        double fb = getFunction().value(b);
        double x = (a * fb - b * fa) / (fb - fa);
        double fx = getFunction().value(x);
        if (fa * fb >= 0) {
            throw new IllegalArgumentException("Invalid interval specified.");
        }
        for (int i = 0; i < getMaxIterations(); i++) {
            if (fx == 0) {
                return x;
            } else if (Math.abs(fx) < getPrecision()) {
                return x;
            } else if (Math.abs(b - a) < getPrecision()) {
                return x;
            } else if (fa * fx < 0) {
                b = x;
                fb = getFunction().value(b);
            } else {
                a = x;
                fa = getFunction().value(a);
            }
            x = (a * fb - b * fa) / (fb - fa);
            fx = getFunction().value(x);
        }
        return x;
    }
}
